package java3.io;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Loserchess {

    public static final int pawn = 1;
    public static final int knight = 2;
    public static final int bishop = 3;
    public static final int rook = 4;
    public static final int queen = 5;
    public static final int king = 6;
    public static final int white = 0;
    public static final int black = 1;
    public static final int vacio = 0;
    public static final int sincolor = 2;
    public boolean noEsJugadaValida;
    public boolean fin_de_ciclo;
    public boolean esAgresivo;
    public boolean[] esFinHilo;
    public int color;
    public int rival;
    public int oo;
    public int ooo;
    public int _oo;
    public int _ooo;
    private int _limiteagresivo;
    private int _limitedefensivo;
    private Map<Integer, List<Variante>> variantesjugadas;
    private Object[] cachememory;
    private UtilLoser util;
    private CalculoLoser calculo;
    private RecursivoLoser recursivo;

    public Loserchess(int jugador) {
        this.color = jugador;
        this.rival = jugador == white ? black : white;
        if (jugador == white) {
            this.oo = 0;
            this.ooo = 1;
            this._oo = 2;
            this._ooo = 3;
        } else {
            this.oo = 2;
            this.ooo = 3;
            this._oo = 0;
            this._ooo = 1;
        }
        util = new UtilLoser();
        calculo = new CalculoLoser(util);
        recursivo = new RecursivoLoser(util, calculo);
        variantesjugadas = new HashMap<>();
    }

    public int[] jugar(int[][] tablero, int jugada_ini, int jugada_fin, boolean[] enroques) {

        Jugadas jugadas;
        List< int[]> jugadasposibles;
        List<int[]> temp;
        int[] t;
        int[] strJug = null;
        int i = 0, cnt;

        //mientras sea falso
        if (!esAgresivo) {
            esAgresivo = util.valorarTableroCompleto(tablero);
        } else {//verificamos si es el final
            if (util.valorarTableroAgresivo(tablero, color) == 1) {
                return null;
            }
        }

        //verificar el cachememory
        if (cachememory != null) {
            for (Object o : cachememory) {
                if (o instanceof List) {
                    temp = (List<int[]>) o;
                    if (temp.size() > 1) {
                        t = temp.get(0);

                        if (t[1] == jugada_ini && t[2] == jugada_fin) {
                            strJug = temp.get(1);
                            //removemos los dos primeros
                            temp.remove(0);
                            temp.remove(0);
                            cachememory = new Object[]{temp};
                            System.out.println("**en cache: " + util.imprimirJugada(strJug));
                            return strJug;
                        }
                    }
                }
            }
        }

        //obtener jugadas posibles
        jugadas = calculo.devolverJugadas(
                tablero, color, jugada_ini, jugada_fin, enroques[oo], enroques[ooo], false);
        jugadasposibles = jugadas.getJugadas();
        cnt = jugadasposibles.size();

        if (cnt > 1) {
            jugadasposibles = util.ordenarMisJugadas(jugadas, tablero);

            esFinHilo = new boolean[cnt];

            noEsJugadaValida = true;
            if (esAgresivo) {//buscamos jugada ganadora
                int a = 0;
                for (int[] j : jugadasposibles) {
                    long l1 = System.currentTimeMillis();
                    final String s2 = util.imprimirJugada(j);
                    final int cc = a;

                    System.out.println("**xxAnalizando " + s2 + "...");
                    fin_de_ciclo = false;
                    _limiteagresivo = 0;

                    //controlando el tiempo
                    Runnable run = new Runnable() {
                        @Override
                        public void run() {
                            int i2 = 0;

                            while (!fin_de_ciclo && i2 < 40) {
                                try {
                                    Thread.sleep(100);
                                    i2++;
                                } catch (Exception ex) {
                                    System.out.println("ex2: " + ex);
                                }
                            }
                            esFinHilo[cc] = true;
                            if (!fin_de_ciclo) {
                                cachememory = null;
                            }
                            
                        }
                    };
                    Thread hilo = new Thread(run, s2);
                    hilo.start();

                    noEsJugadaValida = juegoAgresivo(tablero, j, enroques, cc);
                    fin_de_ciclo = true;

                    System.out.println("fin_ciclo " + cc + ": " + esFinHilo[cc] + "-" + noEsJugadaValida + "-_lim: "+_limiteagresivo);
                    variantesjugadas.clear();

                    System.out.println(System.currentTimeMillis() - l1);
                    if (!noEsJugadaValida && !esFinHilo[cc]) {
                        System.out.println("cache : " + util.mostrarCache(cachememory));
                        strJug = j;
                        break;
                    }
                    a++;
                }
            }

            if (noEsJugadaValida) {
                for (int[] j : jugadasposibles) {
//                    if (i < cnt - 1) {
                        long l1 = System.currentTimeMillis();
                        final String s2 = util.imprimirJugada(j);
                        final int cc = i;
                        CalculoLoser.request = 0;
                        RecursivoLoser.request = 0;
                        System.out.println("**Analizando " + s2 + "...");
                        _limitedefensivo = 0;
//                        fin_de_ciclo = false;
//
//                        //controlando el tiempo
//                        Runnable run = new Runnable() {
//                            @Override
//                            public void run() {
//                                int i2 = 0;
//
//                                while (!fin_de_ciclo && i2 < 20) {
//                                    try {
//                                        Thread.sleep(100);
//                                        i2++;
//                                    } catch (Exception ex) {
//                                        System.out.println("ex2: " + ex);
//                                    }
//                                }
//                                esFinHilo[cc] = true;
//                            }
//                        };
//                        Thread hilo = new Thread(run, s2);
//                        hilo.start();

                        noEsJugadaValida = juegoDefensivo(tablero, j, enroques, cc);
                        fin_de_ciclo = true;

                        System.out.println("fin_ciclo " + cc + ": " + esFinHilo[cc] + "-" + noEsJugadaValida);
                        System.out.println("calc: "+ CalculoLoser.request + "-rec:" + RecursivoLoser.request + "-_lim: "+_limitedefensivo);
                        util.mostrarLog2();
                        variantesjugadas.clear();
                        System.out.println(System.currentTimeMillis() - l1);

                        if (!noEsJugadaValida) {
                            strJug = j;
                            break;
                        }
//                    } else {
//                        System.out.println("Sin analizar " + util.imprimirJugada(j));
//                        strJug = j;
//                    }
                    i++;
                }
            }

        } else if (cnt == 0) {
            System.out.println("** no existen jugadas disponibles");
        } else {
            //devolver la única jugada disponible
            strJug = jugadasposibles.get(0);
            cachememory = null;
        }

        return strJug;
    }

    public boolean juegoDefensivo(int[][] tablero, int[] jugada, boolean[] enroques, int cc) {

        List<int[]> jugadasposiblesRival;
        Jugadas _jugadasposibles;
        boolean[] _enroques;
        int val;

        tablero = util.ingresarJugada(tablero, jugada, true);
        val = util.valorarTableroDefensivo(tablero, rival);

        // si después de mi jugada solo queda el rey rival entonces pruebo con otra jugada
        if (val == 1) {
            return true;
        } else {
            //buscar las jugadas del rival de acuerdo a la jugada elegida al azar
            jugadasposiblesRival = calculo.devolverJugadas(tablero, rival,
                    jugada[1], jugada[2], enroques[_oo], enroques[_ooo], true).getJugadas();
//            System.out.println(util.imprimirJugadas(jugadasposiblesRival));
            if (jugadasposiblesRival.isEmpty()) {
                return true;
            } else {
                //recorro todas las jugadas posibles hasta encontrar una que invalide la
                //jugada elegida al azar en el método previo
                int size = jugadasposiblesRival.size();
                int[][] _tablero;

                util.pintarjugadas(jugadasposiblesRival, 1);
                for (int[] jugada_rival : jugadasposiblesRival) {
                    util.ingresarJugadaLog(0, jugada_rival, false);
                    _tablero = util.ingresarJugada(tablero, jugada_rival, size == 1 ? false : true);

                    //si después de la jugada del rival solo queda mi rey, pruebo con su otra jugada
                    // para validar que con cualquier jugada del rival siempre gano
                    _enroques = util.verificarEnroques(jugada_rival[0], jugada_rival[1], enroques, color == white, size == 1 ? false : true);

                    //obtengo mis jugadas después de la respuesta del rival
                    _jugadasposibles = recursivo.devolverJugadas(_tablero, color, rival, jugada_rival);

                    if (_jugadasposibles.isEsObligatorio() || _jugadasposibles.isHayJaque()) {
                        if (this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, cc, 1)) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    private boolean juegoDefensivoRecursivo(Jugadas jugadasposibles, int[][] tablero,
            boolean[] enroques, int cc, int posi) {
        _limitedefensivo++;
//        String s = _limitedefensivo + "-" + posi + ")"+util.imprimirJugadas(jugadasposibles.getJugadas());
//        System.out.println(s);
//        if (s.equals("10971-19)f8e7,f7f6,g8f6,g8e7,d8c8,d8e8,d8d7,d8c7,"))
//        {   System.out.println(s);
//            System.out.println(util.imprimirTablero(tablero, false));
//            System.out.println("AQUI!!");}
        Jugadas _jugadasposibles;
        List<int[]> jugadasposiblesRival;
        List<int[]> jugadas = jugadasposibles.getJugadas();
        int[][] _tablero;
        boolean[] _enroques;
        boolean eslineavalida = false;
        int[][] t;
        int val;
        String notacionfen = null;
        int num_jugadas = jugadas.size();
        Variante[] variantes = new Variante[num_jugadas];
        int cnt = 0;

//        String s = posi + ")" + util.imprimirJugadas(jugadas);
//        System.out.println(s);
//        if (s.equals("19)e3a7,c8g4,a8a7,")) {
//            System.out.println(util.imprimirTablero(tablero, false));
//        }
        
        util.pintarjugadas(jugadas, 0);
        mis_jugadas:
        for (int[] jugada : jugadas) {
            eslineavalida = false;

            util.ingresarJugadaLog(posi, jugada, true);
            t = util.ingresarJugada(tablero, jugada, num_jugadas == 1 ? false : true);
            val = util.valorarTableroDefensivo(t, rival);

            //si solo le queda el rey, o el rey y ningún peón se invalida la jugada
            if (val == 1 || val == -1) {
                util.mostrarLog(posi, "true");
                return true;
            } else {
                if (posi > 3) {
                    List<Variante> l = variantesjugadas.get(posi);
                    notacionfen = util.obtenerNotacionFEN(t);
                    
                    if (l != null) {
                        for (Variante v : l) {
                            if (notacionfen.equals(v.getNotacionFEN())) {
                                if (v.isResultado()) {
//                                    System.out.println("true:" + posi + "-" + notacionfen + "-" + (l.size()));
                                    util.mostrarLog(posi, "xtrue");
                                    eslineavalida = true;
                                    continue mis_jugadas;
                                } else {
//                                    System.out.println("false:" + posi + "-" + notacionfen + "-" + (l.size()));
                                    util.mostrarLog(posi, "xfalse");
                                    break mis_jugadas;
                                }
                            }
                        }
                    }
                }

                jugadasposiblesRival = calculo.devolverJugadas(t, rival,
                        jugada[1], jugada[2], enroques[_oo], enroques[_ooo], true).getJugadas();

                //si no hay jugadas es porque se dio mate al rival por eso se descarta la jugada
                if (jugadasposiblesRival.isEmpty()) {
                    util.mostrarLog(posi, "true");
                    eslineavalida = true;
                    continue mis_jugadas;
                } else {
                    //recorro todas las jugadas posibles hasta encontrar una que invalide mi jugada elegida
                    int size = jugadasposiblesRival.size();
                    boolean esCopia = size == 1 ? false : true;
                    util.pintarjugadas(jugadasposiblesRival, 1);
                    jugadas_del_rival:
                    for (int[] jugada_rival : jugadasposiblesRival) {
                        util.ingresarJugadaLog(posi + 1, jugada_rival, false);
                        _tablero = util.ingresarJugada(t, jugada_rival, esCopia);
                        _enroques = util.verificarEnroques(jugada_rival[0], jugada_rival[1], enroques, color == white, esCopia);

                        //buscar las jugadas después de la respuesta del rival (mis jugadas)
                        _jugadasposibles = recursivo.devolverJugadas(_tablero, color, rival, jugada_rival);

                        if (_jugadasposibles.isEsObligatorio()) {
                            // si al rival solo le quedan 2 piezas y yo tengo jugada obligatoria, el final es obvio
                            if (val == 2 || esFinHilo[cc]) {
                                util.mostrarLog(posi + 1, "true");
                                eslineavalida = true;
                                break jugadas_del_rival;
                            } else {
                                if (eslineavalida = this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, cc, posi + 2)) {
                                    break jugadas_del_rival;
                                }
                            }
                        } else if (_jugadasposibles.isHayJaque() && !jugadasposibles.isHayJaqueDoble()) {
                            if (jugadasposibles.isHayJaque()) {
                                _jugadasposibles.setHayJaqueDoble(true);
                            }
                            if (eslineavalida = this.juegoDefensivoRecursivo(_jugadasposibles, _tablero, _enroques, cc, posi + 2)) {
                                break jugadas_del_rival;
                            }
                        } else {
                            util.mostrarLog(posi + 1, "false");
                        }
                    }//ffor_rival
                    if (posi > 3) {
                        variantes[cnt] = new Variante(notacionfen, eslineavalida);
                        cnt++;
                    }
                }
            }
            if (!eslineavalida || esFinHilo[cc]) {
                util.mostrarLog(posi + 1, "false");
                break mis_jugadas;
            }
        }//ffor

        if (variantes.length > 0) {
            this.guardarDatos(posi, variantes);
        }

        return eslineavalida;
    }

    public boolean juegoAgresivo(int[][] tablero, int[] jugada, boolean[] enroques, int cc) {

        Jugadas _jugadasposiblesRival;
        boolean[] _enroques;
        int val;

        tablero = util.ingresarJugada(tablero, jugada, true);
        val = util.valorarTableroAgresivo(tablero, rival);

        // si después de mi jugada solo queda el rey rival entonces pruebo con otra jugada
        if (val == 1) {
            return true;
        } else {
            _enroques = util.verificarEnroques(jugada[0], jugada[1], enroques, color == white, true);
            _jugadasposiblesRival = recursivo.devolverJugadas(tablero, rival, color, jugada);

            if (_jugadasposiblesRival.isEsObligatorio() || _jugadasposiblesRival.isHayJaque()) {
                if (!this.juegoAgresivoRecursivo(_jugadasposiblesRival, tablero, _enroques, 0, cc)) {
                    return false;
                }
            }
        }

        return true;
    }

    private boolean juegoAgresivoRecursivo(Jugadas jugadasposiblesrival, int[][] tablero,
            boolean[] enroques, int posi, int cc) {
        _limiteagresivo++;
        Jugadas _jugadasposiblesrival;
        List<int[]> jugadasposibles;
        List<int[]> jugadasrival;
        int[][] _tablero;
        boolean[] _enroques;
        boolean eslineavalida = true;
        String notacionfen = null;
        int num_jugadas;
        Variante[] variantes;
        int[][] t;
        int val;
        int cnt = 0;

        jugadasrival = jugadasposiblesrival.getJugadas();
        num_jugadas = jugadasrival.size();
        variantes = new Variante[num_jugadas];

        if (posi == 0) {
            cachememory = new Object[num_jugadas];
        }
        jugadas_rival:
        for (int[] jugada_rival : jugadasrival) {
            eslineavalida = true;

            util.ingresarJugadaLog(posi, jugada_rival, false);
            t = util.ingresarJugada(tablero, jugada_rival, num_jugadas == 1 ? false : true);
            val = util.valorarTableroAgresivo(t, color);

            if (val == 1) {
                util.mostrarLog(posi, "false");
                return false;
            } else {
                if (posi > 2) {
                    List<Variante> l = variantesjugadas.get(posi);
                    notacionfen = util.obtenerNotacionFEN(t);

                    if (l != null) {
                        for (Variante v : l) {
                            if (notacionfen.equals(v.getNotacionFEN())) {
                                if (v.isResultado()) {
                                    eslineavalida = true;
                                    util.mostrarLog(posi, "xtrue");
                                    break jugadas_rival;
                                } else {
                                    eslineavalida = false;
                                    util.mostrarLog(posi, "xfalse");
                                    continue jugadas_rival;
                                }
                            }
                        }
                    }
                }

                jugadasposibles = calculo.devolverJugadas(t, color,
                        jugada_rival[1], jugada_rival[2], enroques[oo], enroques[ooo], true).getJugadas();

                if (jugadasposibles.isEmpty()) {
                    eslineavalida = false;
                    util.mostrarLog(posi, "false");
                    continue jugadas_rival;
                } else {
                    int size = jugadasposibles.size();
                    boolean esCopia = size == 1 ? false : true;

                    mis_jugadas:
                    for (int[] jugada : jugadasposibles) {
                        util.ingresarJugadaLog(posi + 1, jugada, false);
                        _tablero = util.ingresarJugada(t, jugada, esCopia);
                        _enroques = util.verificarEnroques(jugada[0], jugada[1], enroques, color == white, esCopia);

                        _jugadasposiblesrival = recursivo.devolverJugadas(_tablero, rival, color, jugada);

                        if (_jugadasposiblesrival.isEsObligatorio()) {
                            // si al rival solo le quedan 2 piezas y yo tengo jugada obligatoria, el final es obvio
                            if (val == 2  || esFinHilo[cc]) {
                                eslineavalida = false;
                                util.mostrarLog(posi + 1, "false");
                                break mis_jugadas;
                            } else {
                                if (!(eslineavalida = this.juegoAgresivoRecursivo(_jugadasposiblesrival, _tablero, _enroques, posi + 2, cc))) {
                                    break mis_jugadas;
                                }
                            }
                        } else if (_jugadasposiblesrival.isHayJaque() && !jugadasposiblesrival.isHayJaqueDoble()) {
                            if (jugadasposiblesrival.isHayJaque()) {
                                _jugadasposiblesrival.setHayJaqueDoble(true);
                            }
                            if (!(eslineavalida = this.juegoAgresivoRecursivo(_jugadasposiblesrival, _tablero, _enroques, posi + 2, cc))) {
                                break mis_jugadas;
                            }
                        } else {
                            util.mostrarLog(posi + 1, "true");
                        }
                    }//ffor_rival
                    if (posi > 2) {
                        variantes[cnt] = new Variante(notacionfen, eslineavalida);
                    }
                }
            }
            if (eslineavalida  || esFinHilo[cc]) {
                if (posi == 0) {
                    cachememory = null;
                }
                util.mostrarLog(posi + 1, "true");
                break jugadas_rival;
            }
            if (posi == 0) {
                cachememory[cnt] = util.poblarcache();
            }
            cnt++;
        }//ffor

        if (variantes.length > 0) {
            this.guardarDatos(posi, variantes);
        }

        return eslineavalida;
    }

    private void guardarDatos(int posi, Variante[] var) {
        List<Variante> l = variantesjugadas.get(posi);

        if (l == null) {
            l = new ArrayList<>();
            variantesjugadas.put(posi, l);
        }

        for (Variante v : var) {
            if (v != null) {
                l.add(v);
            }
        }
        //eliminamos los superiores
        if (posi >= 9) {
            int key = posi + 2;
            while (true) {
                if (variantesjugadas.containsKey(key)) {
                    variantesjugadas.put(key, null);
                    key = key + 2;
                } else {
                    break;
                }
            }
        }
    }
}
